package com.basker.pisces.core.query;

import java.util.ArrayList;
import java.util.List;

import org.springframework.util.CollectionUtils;

import com.basker.pisces.collection.AbstractMapList;
import com.basker.pisces.core.query.criteria.Criterion;
import com.basker.pisces.core.query.meta.FieldDesc;
import com.basker.pisces.core.query.meta.QueryMeta;
import com.basker.pisces.core.query.meta.ReferenceFieldDesc;
import com.basker.pisces.core.query.meta.TableDesc;

public class QueryInfo {
    private FetchMode referenceSelectFieldFetchMode = FetchMode.JOIN;

    private List<SelectField> selectFields = new SelectFieldCollection();
    private List<Criterion> criteria;
    private List<GroupField> groupFields;
    private List<OrderField> orderFields;

    private List<SelectField> resolvedSelectFields;

    /**
     * 添加聚合字段
     *
     * @param aggregateField
     * @return
     */
    public QueryInfo addAggregateField(AggregateField aggregateField) {
        return this.addSelectField(aggregateField);
    }

    /**
     * 添加条件对象
     *
     * @param criterion
     * @return
     */
    public QueryInfo addCriterion(Criterion criterion) {
        if (this.criteria == null) {
            this.criteria = new ArrayList<>();
        }

        this.criteria.add(criterion);

        return this;
    }

    /**
     * 添加分组字段
     *
     * @param groupByField
     * @return
     */
    public QueryInfo addGroupByField(GroupField groupByField) {
        if (this.groupFields == null) {
            this.groupFields = new GroupFieldCollection();
        }

        this.groupFields.add(groupByField);

        return this;
    }

    /**
     * 添加排序字段
     *
     * @param orderField
     * @return
     */
    public QueryInfo addOrderField(OrderField orderField) {
        if (this.orderFields == null) {
            this.orderFields = new ArrayList<>();
        }

        this.orderFields.add(orderField);

        return this;
    }

    /**
     * 添加查询字段
     *
     * @param selectField
     * @return
     */
    public QueryInfo addSelectField(SelectField selectField) {
        this.selectFields.add(selectField);

        return this;
    }

    public List<Criterion> getCriteria() {
        return criteria;
    }

    public List<GroupField> getGroupFields() {
        return groupFields;
    }

    public List<OrderField> getOrderFields() {
        return orderFields;
    }

    public FetchMode getReferenceSelectFieldFetchMode() {
        return referenceSelectFieldFetchMode;
    }

    public List<SelectField> getSelectFields() {
        return selectFields;
    }

    public boolean hasCriteria() {
        return !CollectionUtils.isEmpty(criteria);
    }

    /**
     * 是否是分组查询
     *
     * @return
     */
    public boolean isGroupQuery() {
        return !CollectionUtils.isEmpty(this.groupFields);
    }

    /**
     * 引用表的字段是否是select模式加载
     *
     * @return
     */
    public boolean isSelectMode() {
        return FetchMode.SELECT.equals(this.referenceSelectFieldFetchMode);
    }

    public List<SelectField> resolveSelectFields(QueryMeta queryMeta) {
        if (this.resolvedSelectFields != null) {
            return this.resolvedSelectFields;
        }

        // 如果是select模式，需要添加关联引用表的外键字段
        if (this.isSelectMode()) {
            // 聚合引用表上的查询字段
            this.resolvedSelectFields = new ArrayList<>();

            for (SelectField selectField : selectFields) {
                // 先添加需要查询的字段
                this.resolvedSelectFields.add(selectField);

                FieldDesc fieldDesc = queryMeta.getField(selectField.getFieldId(), true);
                if (fieldDesc.getHostTable().isReferenceTable()) {
                    // 添加引用表的外键字段
                    TableDesc refTable = fieldDesc.getHostTable();
                    ReferenceFieldDesc fkField = refTable.getFkField();

                    // 已存在不添加
                    if (resolvedSelectFields.stream().anyMatch(sf -> sf.getFieldId().equals(fkField.getId()))) {
                        continue;
                    }

                    // 将fkSelectField放到selectField前面
                    SelectField fkSelectField = new SelectField(fkField);
                    resolvedSelectFields.add(resolvedSelectFields.indexOf(selectField), fkSelectField);
                }
            }

            return resolvedSelectFields;

        } else {
            this.resolvedSelectFields = selectFields;
            return selectFields;
        }
    }

    public void setCriteria(List<Criterion> criteria) {
        this.criteria = criteria;
    }

    public void setGroupFields(List<GroupField> groupFields) {
        this.groupFields = groupFields;
    }

    public void setOrderFields(List<OrderField> orderFields) {
        this.orderFields = orderFields;
    }

    public void setReferenceSelectFieldFetchMode(FetchMode referenceSelectFieldFetchMode) {
        this.referenceSelectFieldFetchMode = referenceSelectFieldFetchMode;
    }

    public void setSelectFields(List<SelectField> selectFields) {
        this.selectFields = selectFields;
    }

    private static class GroupFieldCollection extends AbstractMapList<String, GroupField> {

        private static final long serialVersionUID = -9079421071015520877L;

        @Override
        protected String getKey(GroupField groupField) {
            return groupField.getFieldId();
        }

    }

    private static class SelectFieldCollection extends AbstractMapList<String, SelectField> {

        private static final long serialVersionUID = -9079421071015520877L;

        @Override
        protected String getKey(SelectField selectField) {
            return selectField.getFieldId();
        }

    }
}
